#include "seg.h"
command *commands = NULL;

static const char *skip_sep(const char *cp){
    while (*cp && isspace(*cp))
        cp++;

    return cp;
}

static const char *skip_arg(const char *cp){
    while (*cp && !isspace(*cp))
        cp++;

    return cp;
}

static int count_argc(const char *str){
    int count = 0;

    while (*str) {
        str = skip_sep(str);
        if (*str) {
            count++;
            str = skip_arg(str);
        }
    }

    return count;
}

void argv_free(char **argv){
    char **p;
    for (p = argv; *p; p++)
        free(*p);

    free(argv);
}

char **argv_split(const char *str, int *argcp){
    int argc = count_argc(str);
    char **argv = malloc(sizeof(*argv) * (argc+1));
    char **argvp;

    if (argv == NULL)
        goto out;

    *argcp = argc;
    argvp = argv;

    while (*str) {
        str = skip_sep(str);

        if (*str) {
            const char *p = str;
            char *t;

            str = skip_arg(str);

            t = strndup(p, (size_t)(str-p));
            if (t == NULL)
                goto fail;
            *argvp++ = t;
        }
    }
    *argvp = NULL;

  out:
    return argv;

  fail:
    argv_free(argv);
    return NULL;
}

void cmd_run(plugin *p, const char *str){
    command* temp_command;
    int argc;
    char *temp;
    char **argv = argv_split(str,&argc);
    if (argc < 1){
        tell(p,"err parsing cmd\n");
        return;
    }
    if (NULL != (temp = strstr(argv[0],SEPARATOR))){
        char *plugin_name = strndup(argv[0],(unsigned)(temp-argv[0]));
        char *cmd_name = strdup(temp+strlen(SEPARATOR));
        remote_cmd(plugin_name,cmd_name,(const char **)argv,argc);
        free(plugin_name);
        free(cmd_name);
    } else if (NULL != (temp_command = cmd_to_command(argv[0]))){
        if ((argc-1) < temp_command->min_args){
            tell(p,"err too few args (min %d)\n",temp_command->min_args);
            return;
        }
        if (temp_command->max_args >= temp_command->min_args && (argc-1) > temp_command->max_args){
            tell(p,"err too many args (max %d)\n",temp_command->max_args);
            return;
        }
        temp_command->function(p,argv,argc);
    } else {
        tell(p,"err invalid command\n");
    }
    argv_free(argv);
}    

int command_add(plugin *p, const char *cmd, void (*function)(), int min_args, int max_args){
    command *temp;

    temp = cmd_to_command(cmd);
    if (temp != NULL){
        tell(p,"err command exists");
        return ERR_EXISTS;
    }

    temp = malloc(sizeof(command));
    temp->cmd = strndup(cmd,MAX_CMD_LEN);
    temp->function = function;
    temp->min_args = min_args;
    temp->max_args = max_args;
    temp->link.prev = NULL;

    printf("added it\n");
    dlink_insert(commands, temp);
    if (NULL == commands) commands = temp;
    return OK;
}

int command_remove(const char *cmd){
    return EINVAL != dlink_remove(cmd_to_command(cmd));
}

void commands_init(void){
    command_add(term,"exit",&cmd_quit,0,0);
    command_add(term,"quit",&cmd_quit,0,0);
    command_add(term,"command_list",&cmd_command_list,0,0);
    command_add(term,"help",&cmd_command_list,0,0);
    command_add(term,"command_add",&cmd_command_add,3,3);
    command_add(term,"command_remove",&cmd_command_remove,1,1);
    command_add(term,"command_exists",&cmd_command_exists,0,0);
}

void cmd_command_add(plugin *p, const char **argv){
    command *temp;
    if (0==strncmp(p->name,"null",strlen("null"))){
        tell(p,"err must identify\n");
        return;
    }
    if (NULL != (temp = cmd_to_command(argv[1]))){
        tell(p,"err command exists in global context\n");
        return;
    }
    /*
    for(temp=(command*)p->commands;temp;temp=temp->next){
        if (0==strncmp(temp->cmd,argv[1],strlen(argv[1]))){
            tell(p,"err command exists in local context\n");
            return;
        }
    }
    */
    /* TODO: figure out what I actually mean */
}

void cmd_command_remove(plugin *p, const char **argv){
    /* TODO: are they allowed? */

}

void cmd_command_exists(plugin *p, const char **argv){
    tell(p,"%d\n",cmd_to_command(argv[0])!=NULL);
}

void cmd_command_list(plugin *p){
    command *temp;

    tell(p,"commands: ");
    for (temp = commands; temp; temp = dlink_next(temp)){
        tell(p,"%s ",temp->cmd);
    }
    tell(p,"\n");
}

void rmnl(char *buf){
    if (buf[strlen(buf)-1] == '\n'){
        buf[strlen(buf)-1] = '\0';
    }
}

command *cmd_to_command(const char *cmd){
    command *temp;

    for (temp = commands; temp; temp = dlink_next(temp)){
        if (0==strcmp(temp->cmd,cmd)){
            printf("found it: %s\n",cmd);
            return temp;
        } else { printf("%s\n",temp->cmd);}
    }
    return NULL;
}

void remote_cmd(char *plugin_name, char *cmd_name, const char **argv, int argc){
}
